<?php
/**
 * Created by 784855684@qq.com.
 * Link: https://github.com/lichtung/wshore
 * User: linzh
 * Date: 7/11/17
 * Time: 10:18 AM
 */
declare(strict_types=1);


namespace application\module\admin\controller;


use application\module\admin\addon\sign\model\UserModel;
use application\module\admin\addon\sign\throws\PasswordErrorException;
use application\module\admin\addon\sign\throws\SigninException;
use application\module\admin\addon\sign\throws\SignupException;
use wshore\core\Email\Email;
use wshore\core\Emailer;
use wshore\core\Logger;
use wshore\core\Response;
use wshore\core\Session;
use wshore\core\view\Render;
use wshore\Exception;
use wshore\helper\Base64x;
use wshore\helper\traits\Redirect;
use application\module\admin\addon\Sign as S;
use wshore\throws\DatabaseException;
use wshore\throws\db\RecordNotFoundException;


class sign
{

    use Render, Redirect;

    /**
     * 登录页面和操作
     * @param string $username 登录账户名
     * @param string $password 登录口令
     * @param string $refer 跳转登录的页面的base64码
     * @param string $token 登录token
     * @param int $remember 记住时间,1:一周,0:一天
     */
    public function in(string $username = '', string $password = '', string $refer = '', string $token = '', int $remember = 0)
    {
        $sign = S::getInstance();
        if ($username and $password and $token) {
            if ($token !== Session::get('token')) {
                if ($token === md5("from_dingding_sns:{$username}{$password}" . SECRET_KEY)) {
                } else {
                    Response::failure('登录页面已经过期,请刷新页面.');# 非法登录(绕过改页面)或者session过期都可能导致该错误
                }
            }
            $status = 0;
            $refer and $refer = Base64x::decode($refer, $sign->encodeKey);
            try {
                strpos($username, '@') === false and Response::failure('使用正确的账号登录');
                try {
                    $sign->in($username, $password, $remember ? 7 * WS_ONE_DAY : WS_ONE_DAY);
                    $status = 1;
                    $message = '';
                    Session::delete('token');
                    $refer or $refer = $sign->getHomePage('/admin/index/index');
                } catch (PasswordErrorException $exception) {
                    $message = "用户'{$username}'的密码错误";
                } catch (RecordNotFoundException $e) {
                    $message = "用户'{$username}'不存在 ";
                } catch (SigninException $exception) {
                    $message = "用户'{$username}'被禁止登录";
                } catch (DatabaseException $exception) {
                    $message = '记录日志失败';
                }
            } catch (\Throwable $e) {
                $message = WS_DEBUG_MODE_ON ? '错误:' . get_class($e) . $e->getMessage() . $e->getTraceAsString() : '系统错误';
            }
            if (WS_IS_AJAX) {
                Response::export([
                    'status' => $status,
                    'message' => $message,
                    'redirect' => $refer,
                ]);
            } else {
                Response::redirect($refer);
            }
        }

        try {
            # 能够从session总读取安全登录的痕迹
            Response::redirect($sign->getHomePage('/admin/index/index'));
        } catch (\Throwable $exception) {
            $this->assign('title', '登录');
            Session::set('token', $token = md5(WS_MEM . $sign->encodeKey));
            $this->assign('token', $token);

            $this->display();
        }
    }

    /**
     * 登出
     * URL:/admin/sign/out
     */
    public function out()
    {
        try {
            Session::set('_auth_explorer_', 0);
            $sign = S::getInstance();
            $sign->out();
        } catch (Exception $exception) {
            //日志
            Logger::getInstance('syserror')->fatal([
                $exception->getMessage(), $exception->getTrace()
            ]);
        } finally {
            Response::redirect('/admin/sign/in');
        }
    }


    public function regetInEmail(string $token = '', string $email = '', string $password = '', string $act = null)
    {
        if ($act) {
            $query = Base64x::decode($act, 'nazest');
            if ($query and $query = json_decode($query, true) and !empty($query['email']) and !empty($query['expire']) and !empty($query['key'])) {
                $email = $query['email'];
                $expire = strtotime($query['expire']);
                $token = $query['key'];
                if ($expire > time()) {
                    try {
                        $user = new UserModel($email);
                        if ($user->meta !== $token) {
                            $error = '邮件失效.';
                        } else {
                            $error = '';
                        }
                    } catch (RecordNotFoundException $e) {
                        $error = "账户'$email'不存在";
                    }
                } else {
                    $error = "邮件于‘{$query['expire']}’过期";
                }

            } else {
                $error = '邮件失效';
            }
            $this->assign([
                'email' => $email??'',
                'error' => $error,
                'token' => empty($token) ? '' : ($token = $error ? '' : $token),//存在错误时 token清0
            ])->display();
        } elseif (WS_IS_AJAX) {
            try {
                $user = new UserModel($email);
                if ($user->meta !== $token) {
                    $error = '邮件失效.';
                } else {
                    # 修改密码和清空token，使得该邮件失效
                    $user->meta = '';
                    $user->password = $password;
                    $user->save();
                    $error = '';
                }
            } catch (RecordNotFoundException $e) {
                $error = "账户'$email'不存在";
            } catch (Exception $e) {
                $error = $e->getMessage();
            }
            $error ? Response::failure($error) : Response::export([
                'message' => '修改成功',
                'status' => 1,
                'redirect' => WS_PUBLIC_URL . '/admin/sign/in',
            ]);
        } else {
            die('404');
        }
    }

    public function forgot(string $email = '', string $token = '')
    {
        $sign = S::getInstance();
        if (WS_IS_AJAX) {
            if ($sign->checkToken($token)) {
                try {
                    # 账户不存在时抛出异常
                    $user = new UserModel($email);

                    $expire_datetime = date('Y-m-d H:i:s', WS_NOW + WS_ONE_DAY);
                    $key = sha1(md5(serialize([
                        $email,
                        $expire_datetime,
                    ])));
                    $user->meta = $key;
                    if (!$user->save()) {
                        Response::failure('系统出错');
                    }

                    $query = Base64x::encode(json_encode([
                        'expire' => $expire_datetime,
                        'email' => $email,
                        'key' => $key,
                    ]), 'nazest');

                    $actlink = WS_PUBLIC_URL . '/admin/sign/regetinemail?act=' . $query;
                    $content = self::loadTemplate('email_reget', [
                        'act' => $actlink,
                        'email' => $email,
                    ]);
                    $_email = new Email('Forgot your account', $content);
                    $_email->addAddress($email);
                    if ((new Emailer())->send($_email)) {
                        Response::export([
                            'status' => 1,
                            'message' => "邮件已经发送到邮箱 '{$email}',请注意查收",
                        ]);
                    } else {
                        Response::failure("邮件'{$email}'发送失败");
                    }

                } catch (RecordNotFoundException $e) {
                    Response::failure("账户'{$email}'不存在");
                }
            } else {
                Response::failure('非法操作');
            }
        }
        $this->assign('title', '找回密码');
        $this->assign('token', $sign->generateToken());
        $this->display();
    }

    public function signup($username = '', $password = '', $email = '')
    {
        if (WS_IS_AJAX) {
            $sign = S::getInstance();
            $data = [];
            $data['username'] = $username;
            $data['password'] = $password;
            $data['email'] = $email;
            $data['disable'] = 1;
            try {
                $sign->up($data);
                $user = new UserModel($username);

                $query = Base64x::encode(json_encode([
                    'id' => $user->id,
                    'email' => $user->email,
                    'username' => $user->username,
                    'disable' => $user->disable,
                ]), 'nazest');

                $actlink = WS_PUBLIC_URL . '/activeinemail?act=' . $query;
                $rmvlink = WS_PUBLIC_URL . '/activeinemail?rmv=' . $query;

                $content = self::loadTemplate('email_active', [
                    'act' => $actlink,
                    'rmv' => $rmvlink,
                    'email' => $email,
                ]);

                $_email = new Email('Active your account', $content);
                $_email->addAddress($email);
                if ((new Emailer())->send($_email)) {
                    Response::export([
                        'status' => 1,
                        'message' => "激活邮件已经发送到邮箱 '{$email}',请注意查收",
                    ]);
                } else {
                    Response::failure("邮件'{$email}'发送失败");
                }
            } catch (SignupException $exception) {
                Response::failure('注册失败');
            }
        }
        $this->assign('title', '注册');
        $this->display();
    }

    public
    function activeInEmail($act = null, $rmv = null)
    {
        if ($act) {
            $data = json_decode(Base64x::decode($act, 'nazest'), true);
            //完整性对比
            $user = new UserModel($data['id']);

            if ($user->username === $data['username'] and
                $user->email === $data['email'] and
                intval($user->disable) === intval($data['disable'])
            ) {
                $user->reload();
                $user->disable = 0;
                if ($user->save()) {
                    $this->success('激活成功', 3, 'Active', '/admin/sign/in');
                } else {
                    $this->failure('你的账户不存在或者已经被激活', 3, 'Active', '/admin/sign/up');
                }
            } else {
                $this->failure('你访问的链接已经失效', 'Active', 3, '/admin/sign/up');
            }

        } elseif ($rmv) {

        } else {
            \wshore::template('code', [
                'code' => 403,
                'title' => '你访问的链接被修改',
                'detail' => 'Active Account',
            ]);
        }
    }

    /**
     * 加载模板
     * @param string $name
     * @param array $search_replace
     * @return mixed
     */
    private
    static function loadTemplate(string $name, array $search_replace)
    {
        $content = file_get_contents(__DIR__ . '/../tpl/' . $name . '.tpl');
        $search = array_keys($search_replace);
        $replace = array_values($search_replace);
        foreach ($search as &$item) {
            $item = "{{{$item}}}";
        }
        return str_replace($search, $replace, $content);
    }

}